{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Tce3stUlHN0L"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "tuOe1ymfHZPu"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qFdPvlXBOdUN"
      },
      "source": [
        "# 勾配と自動微分入門"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MfBg1C5NB3X0"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/autodiff\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org で表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/guide/autodiff.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/guide/autodiff.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/guide/autodiff.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "r6P32iYYV27b"
      },
      "source": [
        "## 自動微分と勾配\n",
        "\n",
        "[自動微分](https://en.wikipedia.org/wiki/Automatic_differentiation)は、ニューラルネットワークをトレーニングする[バックプロパゲーション](https://en.wikipedia.org/wiki/Backpropagation)などの機械学習アルゴリズムの実装に有用です。\n",
        "\n",
        "このガイドでは、特に Eager execution において、TensorFlow を使用して勾配を計算する方法について説明します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MUXex9ctTuDB"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IqR2PQG4ZaZ0"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xHxb-dlhMIzW"
      },
      "source": [
        "## 勾配を計算する\n",
        "\n",
        "TensorFlow は、自動的に微分するために、*フォワード*パス中にどのような演算がどの順序で行われたかを覚えておく必要があります。その後、TensorFlow は*逆方向パス*中にこの演算のリストを逆順に走査し、勾配を計算します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1CLWJl0QliB0"
      },
      "source": [
        "## 勾配テープ\n",
        "\n",
        "TensorFlow には、一部の入力、通常は`tf.Variable`に関する計算の勾配を計算する、自動微分のための [tf.GradientTape](https://www.tensorflow.org/api_docs/python/tf/GradientTape) API があります。TensorFlow は、`tf.GradientTape`のコンテキスト内で実行される関連の演算を「テープ」に「記録」します。その後、TensorFlow はそのテープを使い、[リバースモード微分](https://en.wikipedia.org/wiki/Automatic_differentiation)を使用して「記録」された計算の勾配を計算します。\n",
        "\n",
        "簡単な例を示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Xq9GgTCP7a4A"
      },
      "outputs": [],
      "source": [
        "x = tf.Variable(3.0)\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  y = x**2"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CR9tFAP_7cra"
      },
      "source": [
        "いくつかの演算を記録してから、`GradientTape.gradient(target, sources)`を使用して、いくつかのソース（多くの場合はモデルの変数）に対するいくつかのターゲット（多くの場合は損失）の勾配を計算します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LsvrwF6bHroC"
      },
      "outputs": [],
      "source": [
        "# dy = 2x * dx\n",
        "dy_dx = tape.gradient(y, x)\n",
        "dy_dx.numpy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Q2_aqsO25Vx1"
      },
      "source": [
        "上記の例ではスカラーを使用していますが、どのテンソルでも`tf.GradientTape`は簡単に機能します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vacZ3-Ws5VdV"
      },
      "outputs": [],
      "source": [
        "w = tf.Variable(tf.random.normal((3, 2)), name='w')\n",
        "b = tf.Variable(tf.zeros(2, dtype=tf.float32), name='b')\n",
        "x = [[1., 2., 3.]]\n",
        "\n",
        "with tf.GradientTape(persistent=True) as tape:\n",
        "  y = x @ w + b\n",
        "  loss = tf.reduce_mean(y**2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "i4eXOkrQ-9Pb"
      },
      "source": [
        "両方の変数に対する`y`勾配を取得するために、その両方をソースとして`gradient`メソッドに渡すことができます。テープはソースがどのように渡されるかについて柔軟性があり、リストまたはディクショナリのネストされた組み合わせを受け入れ、同じ方法で構造化された勾配を返します（`tf.nest`を参照）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "luOtK1Da_BR0"
      },
      "outputs": [],
      "source": [
        "[dl_dw, dl_db] = tape.gradient(loss, [w, b])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ei4iVXi6qgM7"
      },
      "source": [
        "各ソースに関する勾配には、ソースの形状があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "aYbWRFPZqk4U"
      },
      "outputs": [],
      "source": [
        "print(w.shape)\n",
        "print(dl_dw.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dI_SzxHsvao1"
      },
      "source": [
        "下記もまた勾配計算ですが、この例では変数のディクショナリを渡します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "d73cY6NOuaMd"
      },
      "outputs": [],
      "source": [
        "my_vars = {\n",
        "    'w': tf.Variable(tf.random.normal((3, 2)), name='w'),\n",
        "    'b': tf.Variable(tf.zeros(2, dtype=tf.float32), name='b')\n",
        "}\n",
        "\n",
        "grad = tape.gradient(loss, my_vars)\n",
        "grad['b']"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HZ2LvHifEMgO"
      },
      "source": [
        "## モデルに関する勾配\n",
        "\n",
        "[チェックポイント](checkpoint.ipynb)と[エクスポート](saved_model.ipynb)をするために、`tf.Variables`を`tf.Module`あるいはそのサブクラスの 1 つ（`layers.Layer`、`keras.Model`）に収集するのは一般的です。\n",
        "\n",
        "ほとんどの場合は、モデルのトレーニング可能な変数に対する勾配を計算する必要があります。`tf.Module`のすべてのサブクラスはそれらの変数を`Module.trainable_variables`プロパティに集約するため、それらの勾配を数行のコードで計算することができます。 "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JvesHtbQESc-"
      },
      "outputs": [],
      "source": [
        "layer = tf.keras.layers.Dense(2, activation='relu')\n",
        "x = tf.constant([[1., 2., 3.]])\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  # Forward pass\n",
        "  y = layer(x)\n",
        "  loss = tf.reduce_mean(y**2)\n",
        "\n",
        "# Calculate gradients with respect to every trainable variable\n",
        "grad = tape.gradient(loss, layer.trainable_variables)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "PR_ezr6UFrpI"
      },
      "outputs": [],
      "source": [
        "for var, g in zip(layer.trainable_variables, grad):\n",
        "  print(f'{var.name}, shape: {g.shape}')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "f6Gx6LS714zR"
      },
      "source": [
        "<a id=\"watches\"></a>\n",
        "\n",
        "## テープの監視対象を制御する"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "N4VlqKFzzGaC"
      },
      "source": [
        "デフォルトの動作では、トレーニング可能な`tf.Variable`にアクセスした後、全ての演算を記録します。その理由は次の通りです。\n",
        "\n",
        "- 逆方向パスの勾配を計算するために、テープはフォワードパス中のどの演算を記録するか、知っておく必要があります。\n",
        "- テープは中間出力への参照を保持するため、不要な演算を記録する必要はありません。\n",
        "- 最も一般的な使用例として、モデルのトレーニング可能なすべての変数に対する損失の勾配の計算があります。\n",
        "\n",
        "例えば、次の例ではデフォルトで`tf.Tensor`が「監視」されておらず、`tf.Variable`はトレーニング不可能なため、勾配の計算ができません。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Kj9gPckdB37a"
      },
      "outputs": [],
      "source": [
        "# A trainable variable\n",
        "x0 = tf.Variable(3.0, name='x0')\n",
        "# Not trainable\n",
        "x1 = tf.Variable(3.0, name='x1', trainable=False)\n",
        "# Not a Variable: A variable + tensor returns a tensor.\n",
        "x2 = tf.Variable(2.0, name='x2') + 1.0\n",
        "# Not a variable\n",
        "x3 = tf.constant(3.0, name='x3')\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  y = (x0**2) + (x1**2) + (x2**2)\n",
        "\n",
        "grad = tape.gradient(y, [x0, x1, x2, x3])\n",
        "\n",
        "for g in grad:\n",
        "  print(g)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RkcpQnLgNxgi"
      },
      "source": [
        "`GradientTape.watched_variables`メソッドを使用すると、テープが監視している変数を一覧表示できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "hwNwjW1eAkib"
      },
      "outputs": [],
      "source": [
        "[var.name for var in tape.watched_variables()]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NB9I1uFvB4tf"
      },
      "source": [
        "`tf.GradientTape`は、ユーザーが監視対象や非監視対象を制御できるフックを提供します。\n",
        "\n",
        "`tf.Tensor`に関する勾配を記録するには、`GradientTape.watch(x)`を呼び出す必要があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tVN1QqFRDHBK"
      },
      "outputs": [],
      "source": [
        "x = tf.constant(3.0)\n",
        "with tf.GradientTape() as tape:\n",
        "  tape.watch(x)\n",
        "  y = x**2\n",
        "\n",
        "# dy = 2x * dx\n",
        "dy_dx = tape.gradient(y, x)\n",
        "print(dy_dx.numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qxsiYnf2DN8K"
      },
      "source": [
        "逆に、すべての`tf.Variables`を監視するデフォルトの動作を無効にするには、勾配テープの作成時に`watch_accessed_variables=False`を設定します。この計算には 2 つの変数を使用しますが、1 つの変数の勾配のみに接続します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7QPzwWvSEwIp"
      },
      "outputs": [],
      "source": [
        "x0 = tf.Variable(0.0)\n",
        "x1 = tf.Variable(10.0)\n",
        "\n",
        "with tf.GradientTape(watch_accessed_variables=False) as tape:\n",
        "  tape.watch(x1)\n",
        "  y0 = tf.math.sin(x0)\n",
        "  y1 = tf.nn.softplus(x1)\n",
        "  y = y0 + y1\n",
        "  ys = tf.reduce_sum(y)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TRduLbE1H2IJ"
      },
      "source": [
        "`GradientTape.watch`は`x0`で呼び出されなかったため、それに関する勾配は計算されません。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "e6GM-3evH1Sz"
      },
      "outputs": [],
      "source": [
        "# dy = 2x * dx\n",
        "grad = tape.gradient(ys, {'x0': x0, 'x1': x1})\n",
        "\n",
        "print('dy/dx0:', grad['x0'])\n",
        "print('dy/dx1:', grad['x1'].numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2g1nKB6P-OnA"
      },
      "source": [
        "## 中間結果\n",
        "\n",
        "`tf.GradientTape`コンテキスト中で計算された中間の値に対する出力の勾配をリクエストすることもできます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7XaPRAwUyYms"
      },
      "outputs": [],
      "source": [
        "x = tf.constant(3.0)\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  tape.watch(x)\n",
        "  y = x * x\n",
        "  z = y * y\n",
        "\n",
        "# Use the tape to compute the gradient of z with respect to the\n",
        "# intermediate value y.\n",
        "# dz_dx = 2 * y, where y = x ** 2\n",
        "print(tape.gradient(z, y).numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ISkXuY7YzIcS"
      },
      "source": [
        "デフォルトでは、ある`GradientTape`に保持されたリソースは、`GradientTape.gradient()`メソッドが呼び出されるとすぐに解放されます。同じ計算で複数の勾配を計算する場合は、`persistent`（永続的な）勾配テープを作成します。こうすると、テープオブジェクトがガベージコレクションされる時にリソースが解放されるため、`gradient()`メソッドを複数回呼び出すことができます。例を示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zZaCm3-9zVCi"
      },
      "outputs": [],
      "source": [
        "x = tf.constant([1, 3.0])\n",
        "with tf.GradientTape(persistent=True) as tape:\n",
        "  tape.watch(x)\n",
        "  y = x * x\n",
        "  z = y * y\n",
        "\n",
        "print(tape.gradient(z, x).numpy())  # 108.0 (4 * x**3 at x = 3)\n",
        "print(tape.gradient(y, x).numpy())  # 6.0 (2 * x)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "j8bv_jQFg6CN"
      },
      "outputs": [],
      "source": [
        "del tape   # Drop the reference to the tape"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "O_ZY-9BUB7vX"
      },
      "source": [
        "## パフォーマンスに関する注記\n",
        "\n",
        "- 勾配テープコンテキスト内の演算の実行に伴う、少量のオーバーヘッドがあります。ほとんどの Eager Execution では、これは目立ったコストにはなりませんが、それでもテープコンテキストは必要な領域のみで使用すべきです。\n",
        "\n",
        "- 勾配テープは、メモリを使用して入力と出力を含む中間結果を格納し、逆方向パス中に使用します。\n",
        "\n",
        "    `ReLU`などの一部の演算は、中間結果を保持する必要がないため、効率を上げるためにフォワードパス中に削除されます。ただし、テープに`persistent=True`を使用している場合は*何も破棄されない*ため、ピーク時のメモリ使用量が高くなります。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9dLBpZsJebFq"
      },
      "source": [
        "## 非スカラーのターゲットの勾配"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7pldU9F5duP2"
      },
      "source": [
        "勾配は基本的にスカラーの演算です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "qI0sDV_WeXBb"
      },
      "outputs": [],
      "source": [
        "x = tf.Variable(2.0)\n",
        "with tf.GradientTape(persistent=True) as tape:\n",
        "  y0 = x**2\n",
        "  y1 = 1 / x\n",
        "\n",
        "print(tape.gradient(y0, x).numpy())\n",
        "print(tape.gradient(y1, x).numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "COEyYp34fxj4"
      },
      "source": [
        "したがって、複数のターゲットの勾配を求める場合、各ソースの結果は次のようになります。\n",
        "\n",
        "- ターゲットの合計の勾配、あるいは\n",
        "- 各ターゲットの勾配の合計"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "o4a6_YOcfWKS"
      },
      "outputs": [],
      "source": [
        "x = tf.Variable(2.0)\n",
        "with tf.GradientTape() as tape:\n",
        "  y0 = x**2\n",
        "  y1 = 1 / x\n",
        "\n",
        "print(tape.gradient({'y0': y0, 'y1': y1}, x).numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uvP-mkBMgbym"
      },
      "source": [
        "同様に、ターゲットがスカラーでない場合は、合計の勾配が計算されます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DArPWqsSh5un"
      },
      "outputs": [],
      "source": [
        "x = tf.Variable(2.)\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  y = x * [3., 4.]\n",
        "\n",
        "print(tape.gradient(y, x).numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "flDbx68Zh5Lb"
      },
      "source": [
        "これにより、損失を集めた合計の勾配、または要素ごとの損失計算の合計の勾配の取得が容易になります。\n",
        "\n",
        "アイテムごとに個別の勾配が必要な場合は、[Jacobians（ヤコビアン）](advanced_autodiff.ipynb#jacobians)をご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iwFswok8RAly"
      },
      "source": [
        "場合によっては、ヤコビアンをスキップすることができます。要素についての計算の場合、各要素は独立しているため、合計の勾配は入力要素に対する各要素の導関数を与えます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JQvk_jnMmTDS"
      },
      "outputs": [],
      "source": [
        "x = tf.linspace(-10.0, 10.0, 200+1)\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  tape.watch(x)\n",
        "  y = tf.nn.sigmoid(x)\n",
        "\n",
        "dy_dx = tape.gradient(y, x)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "e_f2QgDPmcPE"
      },
      "outputs": [],
      "source": [
        "plt.plot(x, y, label='y')\n",
        "plt.plot(x, dy_dx, label='dy/dx')\n",
        "plt.legend()\n",
        "_ = plt.xlabel('x')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6kADybtQzYj4"
      },
      "source": [
        "## 制御フロー\n",
        "\n",
        "テープは実行時に演算を記録するため、Python 制御フロー（例えば、`if`と`while`の使用など）は自然に処理されます。\n",
        "\n",
        "ここでは、`if`の各ブランチで異なる変数が使用されています。勾配は使用された変数にのみ接続します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ciFLizhrrjy7"
      },
      "outputs": [],
      "source": [
        "x = tf.constant(1.0)\n",
        "\n",
        "v0 = tf.Variable(2.0)\n",
        "v1 = tf.Variable(2.0)\n",
        "\n",
        "with tf.GradientTape(persistent=True) as tape:\n",
        "  tape.watch(x)\n",
        "  if x &gt; 0.0:\n",
        "    result = v0\n",
        "  else:\n",
        "    result = v1**2 \n",
        "\n",
        "dv0, dv1 = tape.gradient(result, [v0, v1])\n",
        "\n",
        "print(dv0)\n",
        "print(dv1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HKnLaiapsjeP"
      },
      "source": [
        "制御文自体は微分不可能なため、勾配ベースのオプティマイザから見えないということに注意してください。\n",
        "\n",
        "上記の例の`x`の値次第で、テープは`result = v0`または`result = v1**2`を記録します。`x`に対する勾配は常に`None`です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8k05WmuAwPm7"
      },
      "outputs": [],
      "source": [
        "dx = tape.gradient(result, x)\n",
        "\n",
        "print(dx)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "egypBxISAHhx"
      },
      "source": [
        "## 勾配が`None`になる\n",
        "\n",
        "ターゲットがソースに接続されていない場合、勾配は`None`になります。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "CU185WDM81Ut"
      },
      "outputs": [],
      "source": [
        "x = tf.Variable(2.)\n",
        "y = tf.Variable(3.)\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  z = y * y\n",
        "print(tape.gradient(z, x))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sZbKpHfBRJym"
      },
      "source": [
        "ここでは明らかに`z`が`x`に接続されていませんが、これほど明白ではないにしても勾配が非接続になりうる場合がいくつかあります。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "eHDzDOiQ8xmw"
      },
      "source": [
        "### 1. 変数をテンソルに置き換えた\n",
        "\n",
        "[「テープの監視対象を制御する」](#watches)のセクションで説明したようにテープは自動的に`tf.Variable`を監視しますが、`tf.Tensor`は監視しません。\n",
        "\n",
        "よくあるエラーの 1 つは、`tf.Variable`を更新するために`Variable.assign`を使用する代わりに、`tf.Variable`を`tf.Tensor`で置き換えてしまうことです。例を示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QPKY4Tn9zX7_"
      },
      "outputs": [],
      "source": [
        "x = tf.Variable(2.0)\n",
        "\n",
        "for epoch in range(2):\n",
        "  with tf.GradientTape() as tape:\n",
        "    y = x+1\n",
        "\n",
        "  print(type(x).__name__, \":\", tape.gradient(y, x))\n",
        "  x = x + 1   # This should be `x.assign_add(1)`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3gwZKxgA97an"
      },
      "source": [
        "### 2. TensorFlowの外で計算をした\n",
        "\n",
        "計算が TensorFlow から出てしまうと、テープは勾配パスを記録することができません。例を示します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jmoLCDJb_yw1"
      },
      "outputs": [],
      "source": [
        "x = tf.Variable([[1.0, 2.0],\n",
        "                 [3.0, 4.0]], dtype=tf.float32)\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  x2 = x**2\n",
        "\n",
        "  # This step is calculated with NumPy\n",
        "  y = np.mean(x2, axis=0)\n",
        "\n",
        "  # Like most ops, reduce_mean will cast the NumPy array to a constant tensor\n",
        "  # using `tf.convert_to_tensor`.\n",
        "  y = tf.reduce_mean(y, axis=0)\n",
        "\n",
        "print(tape.gradient(y, x))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "p3YVfP3R-tp7"
      },
      "source": [
        "### 3. 整数または文字列を使用して勾配を取得した\n",
        "\n",
        "整数と文字列は微分不可能です。計算パスがこれらのデータ型を使用する場合は、勾配を取得できません。\n",
        "\n",
        "文字列は微分不可能だと知っていても、`dtype`を指定していない場合に、うっかり`int`定数や変数を作成してしまう可能性があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9jlHXHqfASU3"
      },
      "outputs": [],
      "source": [
        "# The x0 variable has an `int` dtype.\n",
        "x = tf.Variable([[2, 2],\n",
        "                 [2, 2]])\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  # The path to x1 is blocked by the `int` dtype here.\n",
        "  y = tf.cast(x, tf.float32)\n",
        "  y = tf.reduce_sum(x)\n",
        "\n",
        "print(tape.gradient(y, x))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RsdP_mTHX9L1"
      },
      "source": [
        "TensorFlow は型間で自動的にキャストしないため、実際には、勾配が表示されない代わりに型のエラーが表示されることがよくあります。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WyAZ7C8qCEs6"
      },
      "source": [
        "### 4. ステートフルオブジェクトを使用して勾配を取得した\n",
        "\n",
        "状態は勾配を停止します。ステートフルオブジェクト（stateful object）から読み取る場合、テープはその時点の状態のみを表示することができ、その状態に至るまでの履歴は表示できません。\n",
        "\n",
        "`tf.Tensor`は不変です。一旦作成したテンソルは、変更ができません。*値*は持ちますが、*状態*は持ちません。これまでに説明したすべての演算も状態は持っていません。`tf.matmul`の出力は、その入力のみに依存します。\n",
        "\n",
        "`tf.Variable`は、内部状態とその値を持ちます。変数を使用すると、状態が読み取られます。変数に状態があるため以前の変数は勾配の計算に使用できません。 例を示します。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "C1tLeeRFE479"
      },
      "outputs": [],
      "source": [
        "x0 = tf.Variable(3.0)\n",
        "x1 = tf.Variable(0.0)\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  # Update x1 = x1 + x0.\n",
        "  x1.assign_add(x0)\n",
        "  # The tape starts recording from x1.\n",
        "  y = x1**2   # y = (x1 + x0)**2\n",
        "\n",
        "# This doesn't work.\n",
        "print(tape.gradient(y, x0))   #dy/dx0 = 2*(x1 + x2)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xKA92-dqF2r-"
      },
      "source": [
        "同様に、`tf.data.Dataset`イテレータと`tf.queue`は状態を持つので、それらを通過するテンソルのすべての勾配を停止します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HHvcDGIbOj2I"
      },
      "source": [
        "## 勾配が登録されていない"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aoc-A6AxVqry"
      },
      "source": [
        "一部の`tf.Operation`は**微分不可能として登録されているため**、`None`を返します。その他は**勾配の登録がされていません**。\n",
        "\n",
        "[tf.raw_ops](https://www.tensorflow.org/api_docs/python/tf/raw_ops) のページには、勾配を登録する低レベルの演算が示されています。\n",
        "\n",
        "勾配が登録されていない浮動小数点演算を介して勾配を取得しようとすると、テープは暗黙的に`None`を返す代わりにエラーをスローします。これにより、何かが間違っていることが分かります。\n",
        "\n",
        "例えば、`tf.image.adjust_contrast`関数は、[raw_ops.AdjustContrastv2](https://www.tensorflow.org/api_docs/python/tf/raw_ops#.AdjustContrastv2)をラップしているため、勾配を持つことができますが、勾配が実装されていません。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "HSb20FXc_V0U"
      },
      "outputs": [],
      "source": [
        "image = tf.Variable([[[0.5, 0.0, 0.0]]])\n",
        "delta = tf.Variable(0.1)\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  new_image = tf.image.adjust_contrast(image, delta)\n",
        "\n",
        "try:\n",
        "  print(tape.gradient(new_image, [image, delta]))\n",
        "  assert False   # This should not happen.\n",
        "except LookupError as e:\n",
        "  print(f'{type(e).__name__}: {e}')\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pDoutjzATiEm"
      },
      "source": [
        "この演算で微分する必要がある場合は、勾配を実装して登録する（`tf.RegisterGradient`を使用）か、または他の演算を使用して関数を再実装する必要があります。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GCTwc_dQXp2W"
      },
      "source": [
        "## None の代わりにゼロを取得する"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TYDrVogA89eA"
      },
      "source": [
        "場合によっては、接続されていない勾配で`None`ではなく 0（ゼロ）を取得すると便利です。`unconnected_gradients`引数を使用すると、接続されていない勾配がある場合に何を返すかを決めることができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "U6zxk1sf9Ixx"
      },
      "outputs": [],
      "source": [
        "x = tf.Variable([2., 2.])\n",
        "y = tf.Variable(3.)\n",
        "\n",
        "with tf.GradientTape() as tape:\n",
        "  z = y**2\n",
        "print(tape.gradient(z, x, unconnected_gradients=tf.UnconnectedGradients.ZERO))"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [
        "Tce3stUlHN0L"
      ],
      "name": "autodiff.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
